-module(rec_generator).
-export([generate/2]).
%% public
generate(Mod,Nested)->
      {Records,Cntx}=analysis_module(Mod,Nested),
      lists:foreach(fun(X)->generate_module_by_record(X,Records,Cntx)end,Records).      
 
%%private
   
analysis_module(Mod,Nested)->
      {ok,Cntx}=smerl:for_module(Mod),
      Attrs=element(4,Cntx),  
      Record_types=lists:foldl(fun(X,ACC)->
      case element(3,X) of
         record->[{element(1,element(4,X)),
            lists:map(fun(R)->
                case size(R) of
                  3-> element(3,element(3,R));
                  4-> case {element(1,element(4,R)),Nested} of
                        {record,true}->{element(3,element(3,R)),record,element(3,element(4,R))};
                        _->element(3,element(3,R))
                    end
                end
                  end,
                element(2,element(4,X)))}|ACC];
         _->ACC  
      end end,[],Attrs),
      {Record_types,Cntx}.
 
generate_module_by_record({Record,Fields},All_records,Cntx)->
      Cntx_of_new_module=setelement(2,Cntx,Record),
      Rec_str=atom_to_list(Record), 
      Obj_Fun="object(Record)->\n
                    fun({[H|T],[Hv|Tv]})->("++Rec_str++":H(Record,Hv))({T,Tv});\n
                       ({[],[]})->object(Record);\n   
                       ({Field,Value})->\n"++Rec_str++":Field(Record,Value);\n
                       (self)->\nRecord;\n
                       (Field)->"++Rec_str++":Field(Record)end.",
      Cntx_of_object_fun=add_function(Cntx_of_new_module,Obj_Fun,true),      
      {Cntx_of_functions,New_arg_str,New_body_str}=lists:foldl(fun(X,ACC)->
      generate_getter_setter(X,ACC,Rec_str,All_records)end,
      {Cntx_of_object_fun,"(",""},Fields),      
      New_Fun=lists:concat(["new",drop_comma(New_arg_str),")->#",Rec_str,"{"
          ,drop_comma(New_body_str),"}."]),
      Create_Fun=lists:concat(["create",drop_comma(New_arg_str),")->object(new",
                             drop_comma(New_arg_str),"))."]),     
      Cntx_of_new=add_function(Cntx_of_functions,New_Fun,true),
      Cntx_of_create=add_function(Cntx_of_new,Create_Fun,true),
      smerl:compile(Cntx_of_create).

generate_getter_setter({Field,record,Sub_record},Cntx,Rec_str,All_records)->
      [{_,L}|_]=lists:filter(fun({N,_})->N==Sub_record end, All_records),
      Prefix=lists:concat(["(Record#",Rec_str,".",Field,")"]),
      {C,New_arg,New_body}=Cntx,
      New_cntx=lists:foldl(fun(X,ACC)->
               {Getter,Setter}=getter_setter(X,Prefix,Sub_record),
               Cntx_of_getter=add_function(ACC,Getter,true),      
               Cntx_of_setter=add_function(Cntx_of_getter,Setter,true),
               Cntx_of_setter end,C,L),
      generate_getter_setter(Field,{New_cntx,New_arg,New_body},Rec_str,All_records);

 
generate_getter_setter(X,{ACC,New_arg,New_body},Rec_str,_)->
      Field=atom_to_list(X),
      Vfield="N"++Field,
      {Getter,Setter}=getter_setter(Field,"Record",Rec_str),
      Cntx_of_getter=add_function(ACC,Getter,true),      
      Cntx_of_setter=add_function(Cntx_of_getter,Setter,true),
      {Cntx_of_setter,New_arg++Vfield++",",New_body++Field++"="++Vfield++","}.
   
getter_setter({Name,_,_},Prefix,Rec_str)->
      getter_setter(Name,Prefix,Rec_str,body);  
getter_setter(Name,Prefix,Rec_str)->
      getter_setter(Name,Prefix,Rec_str,body).  
getter_setter(Field,Prefix,Rec_str,body)->  
      {lists:concat([Field,"(Record)->",Prefix,"#",Rec_str,".",Field,".\n"]),
      lists:concat([Field,"(Record,Value)->object(",Prefix,"#",Rec_str,"{",Field,"=Value}).\n"])}.

add_function(Cntx,String,Export)->
   {ok,NewCntx}=smerl:add_func(Cntx,String,Export),
   NewCntx.
drop_comma(Str)->
      lists:sublist(Str,length(Str)-1).